/* 
 * File:   PriorityList.c
 * Author: Muqtafi Akhmad
 */

#include "PriorityList.h"

/**
 * Returns true if the list is empty
 * @param L list
 * @return boolean is list empty
 */
boolean EventListEmpty(EventList L) {
    return ((First(L) == NULL));
}

/**
 * Creates an empty list
 * @param L list
 */
void CreateEventList(EventList * L) {
    First(*L) = NULL;
    Last(*L) = First(*L);
    L->elementCount = 0;
}

/**
 * Allocate a list element for the given event
 * Returns NULL if allocation failed
 * @param X event
 * @return address to the list element
 */
EventAddr Alokasi(infotype X) {
    EventAddr P;
    P = (EventAddr) malloc(sizeof (ElmtEvent));
    if (P != NULL) {
        SetEntryNo(&(Info(P)), GetEntryNo(X));
        SetDateTime(&(Info(P)), GetDateTime(X));
        SetDescription(&(Info(P)), GetDescription(X));
        SetPosition(&(Info(P)), GetPosition(X));
        SetParticipants(&(Info(P)), GetParticipants(X), GetParticipantCount(X));
        Next(P) = NULL;
        Prev(P) = NULL;
    }
    return (P);
}

/**
 * Returns address of list element that contains event with 
 * the given entry number
 * @param L list
 * @param entryNo entry number
 * @return address to list element
 */
EventAddr SearchEventByEntryNo(EventList L, long entryNo) {
    if (EventListEmpty(L)) {
        return NULL;
    } else {
        EventAddr ptr = First(L);
        while (ptr != NULL) {
            if (GetEntryNo(Info(ptr)) == entryNo) {
                break;
            } else {
                ptr = Next(ptr);
            }
        }
        return ptr;
    }
}

/**
 * Returns address of the first event that matches the date time
 * Elements scanned start from the start pointer
 * @param L list
 * @param start the start pointer, NULL to search from the beginning
 * @param dateTime date time
 * @return address to list element
 */
EventAddr SearchFirstEvent(EventList L, EventAddr start, DateTime dateTime) {
    if (EventListEmpty(L)) {
        return NULL;
    } else {
        EventAddr ptr;
        if (start == NULL) {
            ptr = First(L);
        } else {
            ptr = start;
        }
        while (ptr != NULL) {
            if (!DateTimeLessThan(GetDateTime(Info(ptr)), dateTime)) {
                break;
            } else {
                ptr = Next(ptr);
            }
        }
        return ptr;
    }
}

/**
 * Insert a new event ordered ascending by date time
 * @param L list
 * @param X elment
 */
void InsertEvent(EventList *L, infotype X) {
    EventAddr addr = Alokasi(X);
    if (addr != NULL) {
        if (!EventListEmpty(*L)) {
            EventAddr next = First(*L);
            while (next != NULL) {
                if (EventLessThan(X, Info(next))) {
                    InsertBefore(L, addr, next);
                    break;
                } else {
                    next = Next(next);
                }
            }
            if (next == NULL) {
                InsertLast(L, addr);
            }
        } else {
            InsertFirst(L, addr);
        }
    }
}

/**
 * Delete an event from list by the event number
 * @param L list
 * @param entryNo entry number
 */
void DeleteEvent(EventList *L, long entryNo) {
    if (!EventListEmpty(*L)) {
        EventAddr ptr = First(*L);
        while (ptr != NULL) {
            if (GetEntryNo(Info(ptr)) == entryNo) {
                DeleteElement(L, ptr);
                break;
            } else {
                ptr = Next(ptr);
            }
        }
    }
}

/**
 * Deallocate the entire elements in the list
 * @param L list
 */
void Dealokasi(EventList* L) {
    if (!EventListEmpty(*L)) {
        EventAddr p = First(*L);
        do {
            DeleteElement(L, p);
            p = Next(p);
        } while (p != NULL);
    }
}

void InsertBefore(EventList *L, EventAddr P, EventAddr Next) {
    if (L->First == Next) {
        InsertFirst(L, P);
    } else {
        InsertAfter(L, P, Prev(Next));
    }
}

/**
 * Insert a new list element as the next of prec element
 * @param L list 
 * @param P address of inserted element
 * @param Prec address of previous element
 */
void InsertAfter(EventList * L, EventAddr P, EventAddr Prec) {
    Prev(Next(Prec)) = P;
    Next(P) = Next(Prec);
    Prev(P) = Prec;
    Next(Prec) = P;
    L->elementCount++;
}

/**
 * Print event in the list element
 * @param L list
 */
void PrintInfo(EventList L) {
    if (First(L) == NULL) {
        printf("Event list is empty \n");
    } else {
        EventAddr p = First(L);
        do {
            PrintEvent(Info(p));
            p = Next(p);
        } while (p != NULL);
    }
}

/**
 * Insert element as the first element of list
 * @param L list
 * @param P address of element
 */
void InsertFirst(EventList * L, EventAddr P) {
    Next(P) = First(*L);
    if (!EventListEmpty(*L)) {
        Prev(First(*L)) = P;
    } else {
        Last(*L) = P;
    }
    First(*L) = P;
    L->elementCount++;
}

/**
 * Insert element as the last element of list
 * @param L list
 * @param P address of element
 */
void InsertLast(EventList* L, EventAddr P) {
    Prev(P) = Last(*L);
    if (!EventListEmpty(*L)) {
        Next(Last(*L)) = P;
    } else {
        First(*L) = P;
    }
    Last(*L) = P;
    L->elementCount++;
}

/**
 * Dealloc a list element
 * @param ptr pointer to the element
 */
void DeleteElement(EventList* L, EventAddr ptr) {
    if (Last(*L) == First(*L)) {
        Last(*L) = NULL;
        First(*L) = NULL;
    } else if (Last(*L) == ptr) {
        Last(*L) = Prev(ptr);
        Next(Last(*L)) = NULL;
    } else if (First(*L) == ptr) {
        First(*L) = Next(ptr);
        Prev(First(*L)) = NULL;
    } else {
        Next(Prev(ptr)) = Next(ptr);
        Prev(Next(ptr)) = Prev(ptr);
    }
    DestroyEvent(&(Info(ptr)));
    L->elementCount--;
    free(ptr);
}

/**
 * Returns the address of n-th list element from the tail of list
 * @param n the order element from behind
 * @return element address
 */
EventAddr GetLastNAddress(EventList L, int n) {
    if (n >= NbElement(L)) {
        return First(L);
    } else {
        EventAddr tempAddr = Last(L);
        int iter;
        for (iter = 1; iter < n; iter++) {
            tempAddr = Prev(tempAddr);
        }
        return tempAddr;
    }
}

/**
 * Get event given the date, and store them in the array of id
 * @param L EventList
 * @param d Date associated
 * @param id array of entryID
 * @param size_t length of id
 * @return length of entryId found
 */
int GetEventCountInDay(EventList L, DateTime d, long* id, int size_t) {
    int retVal = 0;
    int i;
    
    for (i = 0; i < size_t; i++) {
        id[i] = -999;
    }
    
    if (!EventListEmpty(L)) {
        EventAddr ptr = First(L);
        while (ptr != NULL) {
            if(EqDay(GetDate(d),GetDate(GetDateTime(Info(ptr))))){
                id[retVal++] = GetEntryNo(Info(ptr));
            }
            ptr = Next(ptr);
        }        
    }
    return retVal;
}

